package body Billes is


   function New_Bille (Rayon : Long_Float; Centre : Vecteur_Type; Vitesse : Vecteur_Type; Couleur : Couleur_Objet; Masse : Long_Float := 0.0) return Bille_Type is
      B : Bille_Type;
   begin
      B.Fixe    := False;
      B.Rayon   := Rayon;
      B.Pos  := Centre;
      B.Vitesse := Vitesse;
      B.Couleur := Couleur;

      if Masse = 0.0 then
         B.Masse := 4.0 / 3.0 * 3.14 * Rayon ** 3;
      else
         B.Masse := Masse;
      end if;
      return B;
   end New_Bille;

   function Distance (This : Bille_Type; Objet2 : Bille_Type) return Long_Float is
   begin
      return Norme (Objet2.Pos - This.Pos);
   end Distance;
   pragma Inline (Distance);

   function Distance_Carre (Objet1 : Bille_Type; Objet2 : Bille_Type) return Long_Float is
   begin
      return Norme_Carre (Objet2.Pos - Objet1.Pos);
   end Distance_Carre;
   pragma Inline (Distance_Carre);


   procedure Appliquer_Force (This : in out Bille_Type; Force : Vecteur_Type) is
   begin
      This.Acceleration.Add (Force);
   end;

   procedure Deplacer (This : in out Bille_Type; Vect_Depl : Vecteur_Type) is
   begin
      This.Pos.Add(Vect_Depl);
   end;

   procedure Update_Position (This : in out Bille_Type; Dt : Long_Float) is
   begin
      This.Deplacer(This.Vitesse * Dt);
   end;

   function Se_Rapprochent (This : Bille_Type; O : Bille_Type) return Boolean is
      O1x : Long_Float := This.Pos.X;
      O1y : Long_Float := This.Pos.Y;
      O2x : Long_Float := O.Pos.X;
      O2y : Long_Float := O.Pos.Y;
      V1x : Long_Float := This.Vitesse.X;
      V1y : Long_Float := This.Vitesse.Y;
      V2x : Long_Float := O.Vitesse.X;
      V2y : Long_Float := O.Vitesse.Y;
      A : Long_Float := O2x * (V2x - V1x) + O1x * (V1x - V2x);
      B : Long_Float := O2y * (V2y - V1y) + O1y * (V1y - V2y);
   begin
      return (A + B) < 0.0;
   end;

   function Temps_Avant_Impact (This, B : Bille_Type) return Long_Float is
      T      : long_float := 0.0;
      Vx     : long_float := B.Vitesse.X - This.Vitesse.X;
      Vy     : long_float := B.Vitesse.Y - This.Vitesse.Y;
      Ox     : long_float := B.Pos.X - This.Pos.X;
      Oy     : long_float := B.Pos.Y - This.Pos.Y;
      R      : long_float := B.Rayon + This.Rayon;
      Soluce : Solution_Type;
   begin
      Soluce := Solve (Vy ** 2 + Vx ** 2, 2.0 * (Ox * Vx + Oy * Vy), Ox ** 2 + Oy ** 2 - (R) ** 2);
      case Soluce.Genre  is
         when Double =>
            T := long_float'Min (Soluce.X1, Soluce.X2);
         when Simple =>
            T := Soluce.X1;
         when Aucune =>
            T := long_float'Last;
      end case;
      if T < 0.0 then
         return long_float'Last;
      end if;
      return T;
   end Temps_Avant_Impact;

   procedure Choc (This : in out Bille_Type; B : in out Bille_Type) is
      -- base locale au niveau du choc (u passe par les centres des deux boules);
      U, T           : Vecteur_Type;
      --composante des vecteurs vitesses des objets dans la base (u,v)
      U1, U2, T1, T2 : long_float;
      -- norme de la vitesse du centre de gravit�
      Ug             : long_float;
   begin
      -- Creation du vecteur u
      U := (B.Pos-This.Pos);
      U := U / Norme (U); -- normalise le vecteur;

      -- Creation du vecteur v orthogonal � u
      T := (-U.Y, U.X);

      -- Calcul des composantes u1, u2, t1, t2
      U1 := This.Vitesse.Scalaire(U);
      U2 := B.Vitesse.Scalaire(U);
      T1 := This.Vitesse.Scalaire(T);
      T2 := B.Vitesse.Scalaire(T);

      Ug := (This.Masse * U1 + B.Masse * U2) / (This.Masse + B.Masse);

      --Calcul des nouvelles composantes u1 et u2 (t1 et t2 restent inchang�s)
      U1 := Amortissement_Billes * (2.0 * Ug - U1);
      U2 := Amortissement_Billes * (2.0 * Ug - U2);
      T1 := Friction_Bille * T1;
      T2 := Friction_Bille * T2;

      if not This.Fixe then
         This.Vitesse := ((U1 * U.X + T1 * T.X), (U1 * U.Y + T1 * T.Y));
      end if;
      if not B.Fixe then
         B.Vitesse := ((U2 * U.X + T2 * T.X), (U2 * U.Y + T2 * T.Y));
      end if;
   end Choc;

   procedure Calculer_Bornes (This: in out Bille_Type; Largeur, Hauteur : Long_Float; Nb_Subdiv : Natural; Dt : Long_Float) is
      X     : long_float := This.Pos.X;
      Y     : long_float := This.Pos.Y;
      X2    : long_float := X + This.Vitesse.X * Dt;
      Y2    : long_float := Y + This.Vitesse.Y * Dt;
      L     : long_float := Largeur / long_float (Nb_Subdiv);
      H     : long_float := Hauteur / long_float (Nb_Subdiv);
      H_Max : Natural := Nb_Subdiv - 1;
      L_Max : Natural := Nb_Subdiv - 1;
      R     : T_Rect renames This.Rect;
   begin
      -- calcul des x
      if X2 >= X then
         R.Xmin := Natural (long_float'Max (0.0, long_float'Floor ((X - This.Rayon) / L)));
         R.Xmax := Natural'Min (L_Max, Natural (long_float'Floor ((X2 + This.Rayon) / L)));
      else
         R.Xmin := Natural'Min(Natural(Largeur),Natural (long_float'Max (0.0, long_float'Floor ((X2 - This.Rayon) / L))));
         R.Xmax := integer'Max(0, integer'Min (L_Max, integer (long_float'Floor ((X + This.Rayon) / L))));
      end if;

      -- calcul des y
      if Y2 >= Y then
         R.Ymin := Natural (long_float'Max (0.0, long_float'Floor ((Y - This.Rayon) / H)));
         R.Ymax :=  Natural'Min (H_Max, Natural (long_float'Floor ((Y2 + This.Rayon) / H)));
      else
         R.Ymin := Natural (long_float'Max (0.0, long_float'Floor ((Y2 - This.Rayon) / H)));
         R.Ymax :=  Natural'Min (H_Max, Natural (long_float'Floor ((Y + This.Rayon) / H)));
      end if;
   end Calculer_Bornes;

end Billes;
